package org.jboss.seam.ioc.spring;
import org.jboss.seam.ScopeType;
import org.jboss.seam.core.Init;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* NamespaceHandler for a number of seam features in spring.
*
* @author youngm
*/
public class SeamNamespaceHandler extends NamespaceHandlerSupport
{
public static final String SEAM_SCOPE_POST_PROCESSOR = "org.jboss.seam.ioc.spring.SeamScopePostProcessor";
public static final String SEAM_COMPONENT_POST_PROCESSOR = "org.jboss.seam.ioc.spring.SeamComponentPostProcessor";
public static final String SEAM_COMPONENT_POST_PROCESSOR_BEAN_NAME = "org.jboss.seam.ioc.spring.seamComponentPostProcessor";
/**
* @see org.springframework.beans.factory.xml.NamespaceHandler#init()
*/
public void init()
{
registerBeanDefinitionParser("configure-scopes", new SeamConfigureScopeParser());
registerBeanDefinitionParser("instance", new SeamInstanceBeanDefinitionParser());
registerBeanDefinitionDecorator("component", new SeamComponentBeanDefinitionDecorator());
}
/**
* Registers the SeamScopePostProcessor in this bean factory under the name
* defined in SEAM_SCOPE_POST_PROCESSOR. <seam:configure-scope/>
*
* @see SeamScopePostProcessor
* @author youngm
*/
private static class SeamConfigureScopeParser extends AbstractSimpleBeanDefinitionParser
{
/**
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
*/
@Override
protected Class getBeanClass(Element element)
{
return SeamScopePostProcessor.class;
}
/**
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element,
* org.springframework.beans.factory.support.AbstractBeanDefinition,
* org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition,
ParserContext parserContext) throws BeanDefinitionStoreException
{
return SEAM_SCOPE_POST_PROCESSOR;
}
}
/**
* Makes a SeamFactoryBean available for use in a spring ApplicationContext.
* <seam:instance name="someSeamComponent"/>
*
* @see SeamFactoryBean
* @author youngm
*/
private static class SeamInstanceBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser
{
@Override
protected Class getBeanClass(Element element)
{
return SeamFactoryBean.class;
}
}
/**
* Makes an existing spring bean definition a seam component or provides
* hints in the creation of a seam component. Will use the bean definitions
* name and class by default and the classes annotatated InterceptionType.
*
* If proxy=true will wrap the spring bean in a cglib proxy for safe
* injection into singletons.
*
* <seam:component/>
*
* @author youngm
*/
private static class SeamComponentBeanDefinitionDecorator implements BeanDefinitionDecorator
{
private static final String INTERCEPT_TYPE_ATTR = "intercept";
private static final String SPRING_NAME_ATTR = "spring-name";
private static final String SEAM_NAME_ATTR = "name";
private static final String BEAN_CLASS_ATTR = "class";
private static final String AUTO_CREATE_ATTR = "auto-create";
/**
* @see org.springframework.beans.factory.xml.BeanDefinitionDecorator#decorate(org.w3c.dom.Node,
* org.springframework.beans.factory.config.BeanDefinitionHolder,
* org.springframework.beans.factory.xml.ParserContext)
*/
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition,
ParserContext parserContext)
{
// Add the Seam Component Post Processor to the bean factory if it
// doesn't already exist
if (!parserContext.getRegistry().containsBeanDefinition(
SEAM_COMPONENT_POST_PROCESSOR_BEAN_NAME))
{
Class cls;
try
{
cls = ClassUtils.forName(SEAM_COMPONENT_POST_PROCESSOR);
}
catch (ClassNotFoundException e)
{
throw new IllegalStateException("Unable to load class '"
+ SEAM_COMPONENT_POST_PROCESSOR
+ "' make sure you have the jboss-seam-spring.jar in your classpath.", e);
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDefinition.getPropertyValues().addPropertyValue("order", Ordered.LOWEST_PRECEDENCE);
parserContext.getRegistry().registerBeanDefinition(
SEAM_COMPONENT_POST_PROCESSOR_BEAN_NAME, beanDefinition);
}
// get the optional beanClass
String beanClassName = definition.getBeanDefinition().getBeanClassName();
if (node.getAttributes().getNamedItem(BEAN_CLASS_ATTR) != null)
{
beanClassName = node.getAttributes().getNamedItem(BEAN_CLASS_ATTR).getNodeValue();
}
String beanName = definition.getBeanName();
// get the name of the seam component to create
String seamName = beanName;
if (node.getAttributes().getNamedItem(SEAM_NAME_ATTR) != null)
{
seamName = node.getAttributes().getNamedItem(SEAM_NAME_ATTR).getNodeValue();
}
// get the name of the spring bean to use
String springName = beanName;
if (node.getAttributes().getNamedItem(SPRING_NAME_ATTR) != null)
{
springName = node.getAttributes().getNamedItem(SPRING_NAME_ATTR).getNodeValue();
}
// get the interception type to use
Boolean interceptionType = null;
if (node.getAttributes().getNamedItem(INTERCEPT_TYPE_ATTR) != null)
{
interceptionType = Boolean.valueOf(node.getAttributes().getNamedItem(
INTERCEPT_TYPE_ATTR).getNodeValue());
}
// get the requested scope
ScopeType scope = ScopeType.valueOf(node.getAttributes().getNamedItem("scope")
.getNodeValue());
if (scope != ScopeType.STATELESS
&& !BeanDefinition.SCOPE_PROTOTYPE.equals(definition.getBeanDefinition()
.getScope()))
{
throw new IllegalStateException(
"The spring bean scope must be prototype to use a seam scope other than STATELESS.");
}
if (!(parserContext.getRegistry() instanceof BeanFactory))
{
throw new RuntimeException("For some reason your registry is not a BeanFactory");
}
SpringComponent.addSpringComponent(seamName, springName, beanClassName, scope,
(BeanFactory) parserContext.getRegistry(), interceptionType);
if (node.getAttributes().getNamedItem(AUTO_CREATE_ATTR) != null)
{
if (Boolean.valueOf(node.getAttributes().getNamedItem(AUTO_CREATE_ATTR).getNodeValue()))
{
Init.instance().addAutocreateVariable(seamName);
}
}
return definition;
}
}
}